﻿/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "UsbPort.h"
#include <cfgmgr32.h>
#include "SecureLink/Channel.h"
#include "BurnToolCommon/UsbPacket.h"
#include "../platform/Securec/securec.h"
extern "C" {
#include <hidsdi.h>
};
using namespace UsbPortClass;

#define QUIRK_POLLTIMEOUT  (1<<0)
#define QUIRK_FORCE_DFU11  (1<<1)
#define QUIRK_UTF8_SERIAL  (1<<2)
#define QUIRK_DFUSE_LAYOUT (1<<3)
#define QUIRK_DFUSE_LEAVE  (1<<4)

namespace {
    const int NUM5 = 5;
}

UsbPort::UsbPort(QObject *parent)
    : QObject(parent),
      deviceHandle(nullptr),
      sendTimer(nullptr)

{
    qRegisterMetaType<uint32_t>("uint32_t");
}

UsbPort::~UsbPort()
{
}

bool UsbPort::OpenDevice(const CHAR *path, BOOL openRw)
{
    DWORD desiredAccess = (openRw) ? (GENERIC_WRITE | GENERIC_READ) : 0;
    DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
    deviceHandle = CreateFileA(path, desiredAccess, shareMode, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    if (deviceHandle == INVALID_HANDLE_VALUE) {
        deviceHandle = nullptr;
    }
    return IsOpen();
}

void UsbPort::CloseDevice()
{
    CloseHandle(deviceHandle);
    deviceHandle = nullptr;
}

void UsbPort::UsbThread(uint8_t *buffer, uint16_t bufferLen)
{
    SendFramStruct sendFramStruct;
    if (buffer == nullptr || bufferLen == 0) {
        return;
    }
    if (deviceHandle == nullptr) {
        return;
    }
    sendFramStruct.sendBuffer = new uint8_t[bufferLen]();
    if (sendFramStruct.sendBuffer == nullptr) {
        return;
    }
    sendFramStruct.sendBufferLen = bufferLen;
    if (memcpy_s(sendFramStruct.sendBuffer, sendFramStruct.sendBufferLen, buffer, bufferLen) != EOK) {
        delete []sendFramStruct.sendBuffer;
        sendFramStruct.sendBuffer = nullptr;
        return;
    }
    int prePacket = SEND_BUFFER_SIZE - sizeof(SleFrameHeader) - CRC_SIZE;
    sendFramStruct.total = bufferLen / prePacket;
    if (bufferLen % prePacket != 0) {
        sendFramStruct.total += 1;
    }

    sendFramInfoque.enqueue(sendFramStruct);
    if (sendTimer == nullptr) {
        sendTimer = new QTimer();
        offsetCount = 0;
        SlotSendTimerOut();
        if (sendTimer == nullptr) {
            return;
        }
        sendTimer->setInterval(ms);
        sendTimer->setTimerType(Qt::PreciseTimer);
        QMetaObject::invokeMethod(sendTimer,
            std::bind(static_cast<void(QTimer::*)()>(&QTimer::start), sendTimer), Qt::DirectConnection);
        connect(sendTimer, &QTimer::timeout, this, &UsbPort::SlotSendTimerOut, Qt::DirectConnection);
    }
}

void UsbPort::SlotSendTimerOut()
{
    if (sendFramInfoque.size() == 0) {
        sendTimer->stop();
        delete sendTimer;
        sendTimer = nullptr;
        offsetCount = 0;
        return;
    }
    if (offsetCount < sendFramInfoque.head().total) {
        sendTimer->stop();
        SendData();
        QMetaObject::invokeMethod(sendTimer,
            std::bind(static_cast<void(QTimer::*)()>(&QTimer::start), sendTimer), Qt::DirectConnection);
    } else {
        if (sendFramInfoque.head().sendBuffer != nullptr) {
            delete [] sendFramInfoque.head().sendBuffer;
            sendFramInfoque.head().sendBuffer = nullptr;
            sendFramInfoque.dequeue();
            offsetCount = 0;
        }
    }
}

void UsbPort::SendData()
{
    UsbPacketClass::UsbPacket usbPacket;
    int prePacket = SEND_BUFFER_SIZE - sizeof(SleFrameHeader) - CRC_SIZE;
    int datalen = prePacket;
    if (offsetCount + 1 == sendFramInfoque.head().total && sendFramInfoque.head().sendBufferLen % prePacket != 0) {
        datalen = sendFramInfoque.head().sendBufferLen % prePacket;
    }
    if (offsetCount * prePacket + datalen > sendFramInfoque.head().sendBufferLen) {
        if (sendFramInfoque.head().sendBuffer != nullptr) {
            delete [] sendFramInfoque.head().sendBuffer;
            sendFramInfoque.head().sendBuffer = nullptr;
            sendFramInfoque.dequeue();
            offsetCount = 0;
        }
        return;
    }
    usbPacket.SetData(sendFramInfoque.head().sendBuffer + offsetCount * prePacket, datalen);
    PBYTE sendBuffer = usbPacket.GetBuffer(sendFramInfoque.head().total, offsetCount);
    int sendBufferLen = usbPacket.GetBufferSize();
    DWORD long1 = 0;
    OVERLAPPED writeOl;
    writeOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
    int overlapped = FALSE;
    int retwrite = WriteFile(deviceHandle, sendBuffer, sendBufferLen, &long1, &writeOl);
    if (retwrite == 0) {
        if (GetLastError() != ERROR_IO_PENDING) {
            return;
        }
        overlapped = TRUE;
    }
    DWORD bytesWritten = 0;
    if (overlapped == TRUE) {
        int res = WaitForSingleObject(writeOl.hEvent, 1000);
        if (res != WAIT_OBJECT_0) {
            return;
        }
        res = GetOverlappedResult(deviceHandle, &writeOl, &bytesWritten, FALSE);
    }
    offsetCount++;
}
void UsbPort::ReadThread()
{
    while (1) {
        if (deviceHandle == nullptr) {
            break;
        }
        OVERLAPPED writeOl;
        writeOl.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
        DWORD bufferSize = 512;
        uint8_t *buffer = new uint8_t[bufferSize];
        if (buffer == nullptr) {
            break;
        }
        errno_t errMemset = memset_s(buffer, bufferSize, 0, bufferSize);
        if (errMemset != EOK) {
            break;
        }
        buffer[0] = 0x1f;
        int retRead = HidD_GetInputReport(deviceHandle, buffer, bufferSize);
        if (retRead == 0) {
            if (GetLastError() != ERROR_IO_PENDING) {
                if (buffer != nullptr) {
                    delete []buffer;
                    buffer = nullptr;
                }
                Sleep(NUM5);
                continue;
            }
        }
        DWORD i = 0;
        for (; i < bufferSize; i++) {
            if (buffer[i] != 0) {
                emit SignalReadData(buffer, bufferSize);
                break;
            }
        }
        if (i == bufferSize) {
            delete []buffer;
            buffer = nullptr;
        }
        Sleep(NUM5); // 读取间隔5ms
    }
}

bool UsbPort::IsOpen() const
{
    if (deviceHandle == nullptr) {
        return false;
    }
    return true;
}

void UsbPort::SetMs(int sleepms)
{
    ms = sleepms;
}
